package in.muton.edushare.auth.roles;

import in.muton.edushare.EduShareSession;

import java.util.List;

import com.edushare.common.dao.AssignmentsDAO;
import com.edushare.common.dao.CourseInstancesDAO;
import com.edushare.common.dao.DepartmentsDAO;
import com.edushare.common.dao.GroupsDAO;
import com.edushare.common.dao.UniversitiesDAO;
import com.edushare.common.dao.UsersDAO;
import com.edushare.common.exceptions.UnknownFileOwnerTypeException;
import com.edushare.common.orm.ORMInterface;
import com.edushare.common.util.StringUtil;
import com.edushare.common.vo.Assignments;
import com.edushare.common.vo.CourseDeptLink;
import com.edushare.common.vo.CourseInstances;
import com.edushare.common.vo.Courses;
import com.edushare.common.vo.Departments;
import com.edushare.common.vo.FileDirectories;
import com.edushare.common.vo.FilesUploaded;
import com.edushare.common.vo.Groups;
import com.edushare.common.vo.UnivDeptLink;
import com.edushare.common.vo.Universities;
import com.edushare.common.vo.UniversityCourseLinks;
import com.edushare.common.vo.Users;
import com.edushare.common.vo.FileDirectories.DIRECTORY_OWNER_TYPE;

public class FileAccessCheck {
	
	private static final int ROLE_ALL			= 0;//default: all in the group
	private static final int ROLE_STUDENT 		= 10;
	private static final int ROLE_TEACHER 		= 20;
	private static final int ROLE_ADMIN 		= 30;
	private static final int ROLE_MYSELF 		= 40;
	private static final int ROLE_GROUP_MEMBER	= 50;
	
	private static final int SCOPE_DEFAULT		= 0; //default: scope == current group only
	private static final int SCOPE_UNIV 		= 10;
	private static final int SCOPE_DEPT 		= 20;
	private static final int SCOPE_COURSE 		= 30;
	private static final int SCOPE_MYSELF 		= 40;
	private static final int SCOPE_GROUP 		= 50;
	
	
	public boolean isAccessible(Users accessingUser, FilesUploaded f) 
	throws UnknownFileOwnerTypeException
	{
		ORMInterface ormInterface = EduShareSession.get().getSessionORMInterface();
		FileDirectories fd = f.getFileDirectory();
		int ownerType = fd.getOwnerType();
		int ownerId = fd.getOwnerId();
		String readAccess = fd.getReadPerm();
		int scope = getScope(readAccess);
		int role = getRole(readAccess);
		
		//if public allow access to all
		if(isPublicAccess(fd))
			return true;
		
		//now on login is required
		if(accessingUser==null){
			return false;
		}
		
		//if uploader allow him to see the file
		Users u1 = f.getUploader();
		System.out.println("u1: " + u1 +"\naccessingUser: "+accessingUser+"\naccessingUser.equals(f.getUploader()):"+accessingUser.equals(f.getUploader())+"\nu1==accessingUser:"+(u1==accessingUser));
		if(accessingUser.equals(f.getUploader()))
			return true;
		
		//Admin privileges if scope=dept => deptAdmins & univAdmins are having default permission but not courseAdmin
		// ===> check for isAdminAbove methods
		
		//get the group this file belongs to
		if(ownerType==DIRECTORY_OWNER_TYPE.UNIVERSITY.getIntVal()){
			Universities u = new UniversitiesDAO(ormInterface).getUnivById(ownerId);
			return isAccessible(u, accessingUser, scope, role);
			
		}else if(ownerType==DIRECTORY_OWNER_TYPE.DEPARTMENT.getIntVal()){
			Departments d = new DepartmentsDAO(ormInterface).getDepartmentByPk(ownerId);
			return isAccessible(d, accessingUser, scope, role) || isAdminAbove(d, accessingUser);
			
		}else if (ownerType==DIRECTORY_OWNER_TYPE.COURSE_INSTANCE.getIntVal()){
			CourseInstances ci = new CourseInstancesDAO(ormInterface).getCourseInstanceById(ownerId);
			return isAccessible(ci, accessingUser, scope, role) || isAdminAbove(ci, accessingUser);
			
		}else if(ownerType==DIRECTORY_OWNER_TYPE.USER.getIntVal()){
			Users u = new UsersDAO(ormInterface).getUserByPk(ownerId);
			return isAccessible(u, accessingUser, scope, role);
			
		}else if(ownerType==DIRECTORY_OWNER_TYPE.ASSIGNMENT.getIntVal()){
			//Assignments have same permissions as course instances
			Assignments a = new AssignmentsDAO(ormInterface).getAssignmentByAssignmentId(ownerId);
			CourseInstances ci = a.getCourseInstance();
			return isAccessible(ci, accessingUser, scope, role) || isAdminAbove(ci, accessingUser);
			
		}else if(ownerType==DIRECTORY_OWNER_TYPE.GROUP.getIntVal()){
			Groups g = new GroupsDAO(ormInterface).getGroupById(ownerId);
			return isAccessible(g, accessingUser, scope, role) || isAdminAbove(g, accessingUser);
			
		}else{
			throw new UnknownFileOwnerTypeException("Please Make Sure FileDirectories.DIRECTORY_OWNER_TYPE has this class registered!");
		}
		
//		return false;
	}

	private boolean isAdminAbove(Groups g, Users accessingUser) {
		return UnivRoleCheck.isAdmin(g.getUniversity(), accessingUser);
	}

	/** returns true if the user is Admin of dept/univ, this course belongs to */
	private boolean isAdminAbove(CourseInstances ci, Users accessingUser) {
		boolean isAdminAbove = false;
		Courses c = ci.getCourse();
		for(CourseDeptLink cdl: c.getDeptLinks()){
			DeptRoleCheck crc = new DeptRoleCheck(cdl.getDepartment(), accessingUser);
			if(crc.isPrimaryAdmin() || crc.isSecondaryAdmin()){
				isAdminAbove = true;
				break;
			}
		}
		
		//if not a department admin, check for univ admin
		if(!isAdminAbove){
			for(UniversityCourseLinks ucl: c.getUniversities()){
				UnivRoleCheck urc = new UnivRoleCheck(ucl.getUniversity(), accessingUser);
				if(urc.isAdmin()){
					isAdminAbove = true;
					break;
				}
			}
		}
		
		return isAdminAbove;
	}

	/** returns true if the user is Admin of any University where this dept belongs */
	private boolean isAdminAbove(Departments d, Users accessingUser) {
		boolean isAdminAbove = false;
		for(UnivDeptLink udl: d.getUnivLinks()){
			UnivRoleCheck urc = new UnivRoleCheck(udl.getUniversity(), accessingUser);
			if(urc.isAdmin()){
				isAdminAbove = true;
				break;
			}
		}
		return isAdminAbove;
	}
	

	/**
	 * Accessibility test when file owner is a user
	 * @param u
	 * @param accessingUser
	 * @param scope
	 * @param role
	 * @return
	 */
	private boolean isAccessible(Users owner, Users accessingUser, int scope, int role) {
		//let all the files of an user to be accessible to him
		if(owner.equals(accessingUser)){
			return true;
		}
		
		if(scope == SCOPE_MYSELF){
			return UserUserRelationshipCheck.isMyself(owner, accessingUser);
		}else if(scope==SCOPE_COURSE){
			return UserUserRelationshipCheck.isSharedCourseInstance(owner, accessingUser);
		}else if (scope==SCOPE_DEPT){
			return UserUserRelationshipCheck.isSharedDepartment(owner, accessingUser);
		}else if(scope==SCOPE_UNIV){
			return UserUserRelationshipCheck.isSharedUniv(owner, accessingUser);
		}
		
		return false;
	}

	/**
	 * 
	 * @param group
	 * @param accessingUser
	 * @param scope
	 * @param role
	 * @return
	 */
	private boolean isAccessible(Groups g, Users accessingUser, int scope, int role) {
		if(scope == SCOPE_GROUP){
			if(role == ROLE_GROUP_MEMBER){
				return accessingUser.isGroupMember(g);
			}else if(scope == SCOPE_UNIV){
				isAccessible(g.getUniversity(), accessingUser, scope, role);
			}
		}
		return false;
	}

	/**
	 * Accessibility test when file owner is a CourseInstance
	 * @param ci
	 * @param accessingUser
	 * @param scope
	 * @param role
	 * @return true if accessible
	 */
	private boolean isAccessible(CourseInstances ci, Users accessingUser, int scope, int role) {
		if(scope == SCOPE_COURSE || scope == SCOPE_DEFAULT){
			CourseRoleCheck crolecheck = new CourseRoleCheck(ci, accessingUser);
			if(role == ROLE_STUDENT)
				return crolecheck.isStudent() || crolecheck.isTeacher() || crolecheck.isAdmin();
			else if(role == ROLE_TEACHER)
				return crolecheck.isTeacher() || crolecheck.isAdmin();
			else if(role == ROLE_ADMIN)
				return crolecheck.isAdmin();
		}else if(scope == SCOPE_DEPT){
			List<CourseDeptLink> cdll = ci.getCourse().getDeptLinks();
			boolean isAccessible = false;
			for(CourseDeptLink cdl:cdll){
				isAccessible = isAccessible(cdl.getDepartment(), accessingUser, scope, role);
				if(isAccessible)
					break;
			}
			return isAccessible;
		}else if (scope == SCOPE_UNIV){
			List<UniversityCourseLinks> ucll = ci.getCourse().getUniversities();
			boolean isAccessible = false;
			for(UniversityCourseLinks ucl: ucll){
				isAccessible = isAccessible(ucl.getUniversity(), accessingUser, scope, role);
				if(isAccessible)
					break;
			}
			return isAccessible;
		}
		
		return false;
	}

	/**
	 * File accessibility when owner is a department
	 * @param d
	 * @param accessingUser
	 * @param scope
	 * @param role
	 * @return
	 */
	private boolean isAccessible(Departments d, Users accessingUser, int scope, int role) {
		if(scope==SCOPE_DEPT || scope ==SCOPE_DEFAULT){
			DeptRoleCheck drolecheck = new DeptRoleCheck(d, accessingUser);
			if(role == ROLE_STUDENT)
				return drolecheck.isMember();
			else if(role == ROLE_TEACHER)
				return drolecheck.isTeacher()|| drolecheck.isPrimaryAdmin() || drolecheck.isSecondaryAdmin();
			else if(role == ROLE_ADMIN)
				return drolecheck.isPrimaryAdmin() || drolecheck.isSecondaryAdmin();
			else 
				return false;
		}else if(scope==SCOPE_UNIV){
			List<UnivDeptLink> ul = d.getUnivLinks();
			boolean isAccessible = false;
			for(UnivDeptLink udl: ul){
				isAccessible = isAccessible(udl.getUniversity(), accessingUser, scope, role);
				if(isAccessible)
					break;
			}
			return isAccessible;
		}
		return false;
	}

	/**
	 * File Accessibility when file Owner is an University 
	 * @param u
	 * @param accessingUser
	 * @param scope
	 * @param role
	 * @return
	 */
	private boolean isAccessible(Universities u, Users accessingUser, int scope, int role) {
		UnivRoleCheck rolecheck = new UnivRoleCheck(u, accessingUser);
		if(role == ROLE_STUDENT)
			return rolecheck.isMember();
		else if(role == ROLE_TEACHER)
			return rolecheck.isTeacher() ||  rolecheck.isAdmin();
		else if(role == ROLE_ADMIN)
			return rolecheck.isAdmin();
		else 
			return false;
	}

	/**
	 * Gets role from two lettered access code
	 * @param readAccess
	 * @return
	 */
	private int getRole(String readAccess) {
		if(StringUtil.isValid(readAccess) && readAccess.length()==2){
			if(readAccess.startsWith(FileDirectories.ROLE_STUDENT)){
				return ROLE_STUDENT;
			}else if(readAccess.startsWith(FileDirectories.ROLE_TEACHER)){
				return ROLE_TEACHER;
			}else if(readAccess.startsWith(FileDirectories.ROLE_ADMIN)){
				return ROLE_ADMIN;
			}else if(readAccess.startsWith(FileDirectories.ROLE_MYSELF)){
				return ROLE_MYSELF;
			}else if(readAccess.startsWith(FileDirectories.ROLE_GROUP_MEMBER)){
				return ROLE_GROUP_MEMBER;
			}else if(readAccess.startsWith(FileDirectories.ROLE_ANY)){
				return ROLE_ALL;
			}
		}
		return 0;
	}

	/**
	 * gets scope from two lettered access code
	 * @param readAccess
	 * @return
	 */
	private int getScope(String readAccess) {
		if(StringUtil.isValid(readAccess) && readAccess.length()==2){
			if(readAccess.endsWith(FileDirectories.SCOPE_COURSE)){
				return SCOPE_COURSE;
			}else if(readAccess.endsWith(FileDirectories.SCOPE_DEPT)){
				return SCOPE_DEPT;
			}else if(readAccess.endsWith(FileDirectories.SCOPE_UNIV)){
				return SCOPE_UNIV;
			}else if(readAccess.endsWith(FileDirectories.SCOPE_MYSELF)){
				return SCOPE_MYSELF;
			}else if(readAccess.endsWith(FileDirectories.SCOPE_GROUP)){
				return SCOPE_GROUP;
			}else {
				return SCOPE_DEFAULT;
			}
		}
		return 0;
	}

	/**
	 * is file publicly available?
	 * @param fd
	 * @return
	 */
	private boolean isPublicAccess(FileDirectories fd) {
		return fd.getReadPerm().contains(FileDirectories.PERM_PUBLIC);
	}
	
	
	
}
